#include "qtermzmodem.h"
#include "qtermtelnet.h"
#include "qtermframe.h"
#include "qterm.h"
#include "qtermconfig.h"
#include "qtermglobal.h"

#include <QtCore/QString>
#include <QtCore/QFileInfo>
#include <QtCore/QTextCodec>
#include <QFileDialog>

namespace QTerm
{

#ifdef QTERM_DEBUG // for zmodemlog()
#include <sys/time.h>
#endif

/*
 *  Crc calculation stuff
 */

/* crctab calculated by Mark G. Mendel, Network Systems Corporation */
unsigned short crctab[256] = {
    0x0000,  0x1021,  0x2042,  0x3063,  0x4084,  0x50a5,  0x60c6,  0x70e7,
    0x8108,  0x9129,  0xa14a,  0xb16b,  0xc18c,  0xd1ad,  0xe1ce,  0xf1ef,
    0x1231,  0x0210,  0x3273,  0x2252,  0x52b5,  0x4294,  0x72f7,  0x62d6,
    0x9339,  0x8318,  0xb37b,  0xa35a,  0xd3bd,  0xc39c,  0xf3ff,  0xe3de,
    0x2462,  0x3443,  0x0420,  0x1401,  0x64e6,  0x74c7,  0x44a4,  0x5485,
    0xa56a,  0xb54b,  0x8528,  0x9509,  0xe5ee,  0xf5cf,  0xc5ac,  0xd58d,
    0x3653,  0x2672,  0x1611,  0x0630,  0x76d7,  0x66f6,  0x5695,  0x46b4,
    0xb75b,  0xa77a,  0x9719,  0x8738,  0xf7df,  0xe7fe,  0xd79d,  0xc7bc,
    0x48c4,  0x58e5,  0x6886,  0x78a7,  0x0840,  0x1861,  0x2802,  0x3823,
    0xc9cc,  0xd9ed,  0xe98e,  0xf9af,  0x8948,  0x9969,  0xa90a,  0xb92b,
    0x5af5,  0x4ad4,  0x7ab7,  0x6a96,  0x1a71,  0x0a50,  0x3a33,  0x2a12,
    0xdbfd,  0xcbdc,  0xfbbf,  0xeb9e,  0x9b79,  0x8b58,  0xbb3b,  0xab1a,
    0x6ca6,  0x7c87,  0x4ce4,  0x5cc5,  0x2c22,  0x3c03,  0x0c60,  0x1c41,
    0xedae,  0xfd8f,  0xcdec,  0xddcd,  0xad2a,  0xbd0b,  0x8d68,  0x9d49,
    0x7e97,  0x6eb6,  0x5ed5,  0x4ef4,  0x3e13,  0x2e32,  0x1e51,  0x0e70,
    0xff9f,  0xefbe,  0xdfdd,  0xcffc,  0xbf1b,  0xaf3a,  0x9f59,  0x8f78,
    0x9188,  0x81a9,  0xb1ca,  0xa1eb,  0xd10c,  0xc12d,  0xf14e,  0xe16f,
    0x1080,  0x00a1,  0x30c2,  0x20e3,  0x5004,  0x4025,  0x7046,  0x6067,
    0x83b9,  0x9398,  0xa3fb,  0xb3da,  0xc33d,  0xd31c,  0xe37f,  0xf35e,
    0x02b1,  0x1290,  0x22f3,  0x32d2,  0x4235,  0x5214,  0x6277,  0x7256,
    0xb5ea,  0xa5cb,  0x95a8,  0x8589,  0xf56e,  0xe54f,  0xd52c,  0xc50d,
    0x34e2,  0x24c3,  0x14a0,  0x0481,  0x7466,  0x6447,  0x5424,  0x4405,
    0xa7db,  0xb7fa,  0x8799,  0x97b8,  0xe75f,  0xf77e,  0xc71d,  0xd73c,
    0x26d3,  0x36f2,  0x0691,  0x16b0,  0x6657,  0x7676,  0x4615,  0x5634,
    0xd94c,  0xc96d,  0xf90e,  0xe92f,  0x99c8,  0x89e9,  0xb98a,  0xa9ab,
    0x5844,  0x4865,  0x7806,  0x6827,  0x18c0,  0x08e1,  0x3882,  0x28a3,
    0xcb7d,  0xdb5c,  0xeb3f,  0xfb1e,  0x8bf9,  0x9bd8,  0xabbb,  0xbb9a,
    0x4a75,  0x5a54,  0x6a37,  0x7a16,  0x0af1,  0x1ad0,  0x2ab3,  0x3a92,
    0xfd2e,  0xed0f,  0xdd6c,  0xcd4d,  0xbdaa,  0xad8b,  0x9de8,  0x8dc9,
    0x7c26,  0x6c07,  0x5c64,  0x4c45,  0x3ca2,  0x2c83,  0x1ce0,  0x0cc1,
    0xef1f,  0xff3e,  0xcf5d,  0xdf7c,  0xaf9b,  0xbfba,  0x8fd9,  0x9ff8,
    0x6e17,  0x7e36,  0x4e55,  0x5e74,  0x2e93,  0x3eb2,  0x0ed1,  0x1ef0
};

/*
 * updcrc macro derived from article Copyright (C) 1986 Stephen Satchell.
 *  NOTE: First argument must be in range 0 to 255.
 *        Second argument is referenced twice.
 *
 * Programmers may incorporate any or all code into their programs,
 * giving proper credit within the source. Publication of the
 * source routines is permitted so long as proper credit is given
 * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg,
 * Omen Technology.
 */

#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp)

/*
 * Copyright (C) 1986 Gary S. Brown.  You may use this program, or
 * code or tables extracted from it, as desired without restriction.
 */

/* First, the polynomial itself and its table of feedback terms.  The  */
/* polynomial is                                                       */
/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */
/* Note that we take it "backwards" and put the highest-order term in  */
/* the lowest-order bit.  The X^32 term is "implied"; the LSB is the   */
/* X^31 term, etc.  The X^0 term (usually shown as "+1") results in    */
/* the MSB being 1.                                                    */

/* Note that the usual hardware shift register implementation, which   */
/* is what we're using (we're merely optimizing it by doing eight-bit  */
/* chunks at a time) shifts bits into the lowest-order term.  In our   */
/* implementation, that means shifting towards the right.  Why do we   */
/* do it this way?  Because the calculated CRC must be transmitted in  */
/* order from highest-order term to lowest-order term.  UARTs transmit */
/* characters in order from LSB to MSB.  By storing the CRC this way,  */
/* we hand it to the UART in the order low-byte to high-byte; the UART */
/* sends each low-bit to hight-bit; and the result is transmission bit */
/* by bit from highest- to lowest-order term without requiring any bit */
/* shuffling on our part.  Reception works similarly.                  */

/* The feedback terms table consists of 256, 32-bit entries.  Notes:   */
/*                                                                     */
/*     The table can be generated at runtime if desired; code to do so */
/*     is shown later.  It might not be obvious, but the feedback      */
/*     terms simply represent the results of eight shift/xor opera-    */
/*     tions for all combinations of data and CRC register values.     */
/*                                                                     */
/*     The values must be right-shifted by eight bits by the "updcrc"  */
/*     logic; the shift must be unsigned (bring in zeroes).  On some   */
/*     hardware you could probably optimize the shift in assembler by  */
/*     using byte-swap instructions.                                   */

unsigned long cr3tab[] = { /* CRC polynomial 0xedb88320 */
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
    0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
    0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
    0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
    0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
    0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
    0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
    0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
    0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
    0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
    0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
    0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
    0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
    0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
    0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
    0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
    0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
    0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
    0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
    0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
    0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
    0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};

#define UPDC32(b, c) (cr3tab[((int)c ^ b) & 0xff] ^ ((c >> 8) & 0x00FFFFFF))

#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp)



#if 1
StateTable Zmodem::RStartOps[] =  {
    {ZSINIT, &Zmodem::GotSinit, 0, 1, RSinitWait}, /* SINIT, wait for attn str */
    {ZFILE, &Zmodem::GotFile, 0, 0, RFileName}, /* FILE, wait for filename */
    {ZRQINIT, &Zmodem::SendRinit, 0, 1, RStart}, /* sender confused, resend */
    {ZFIN, &Zmodem::GotFin, 1, 0, RFinish},  /* sender shutting down */
    {ZNAK, &Zmodem::SendRinit, 1, 0, RStart},  /* RINIT was bad, resend */
    {ZFREECNT, &Zmodem::GotFreecnt, 0, 0, RStart}, /* sender wants free space */
    {ZCOMMAND, &Zmodem::GotCommand, 0, 0, CommandData}, /* sender wants command */
    {ZSTDERR, &Zmodem::GotStderr, 0, 0, StderrData}, /* sender wants to send msg */
    {ZRINIT, &Zmodem::ZmodemTInit, 1, 1, TStart},
    {99, &Zmodem::ZPF, 0, 0, RStart},   /* anything else is an error */
} ;

StateTable Zmodem::RSinitWaitOps[] = { /* waiting for data */
    {99, &Zmodem::ZPF, 0, 0, RSinitWait},
} ;

StateTable Zmodem::RFileNameOps[] = { /* waiting for file name */
    {99, &Zmodem::ZPF, 0, 0, RFileName},
} ;

StateTable Zmodem::RCrcOps[] = {  /* waiting for CRC */
    {ZCRC, &Zmodem::GotFileCrc, 0, 0, RFile},    /* sender sent it */
    {ZNAK, &Zmodem::ResendCrcReq, 0, 0, RCrc},    /* ZCRC was bad, resend */
    {ZRQINIT, &Zmodem::SendRinit, 1, 1, RStart},   /* sender confused, restart */
    {ZFIN, &Zmodem::GotFin, 1, 1, RFinish},    /* sender signing off */
    {99, &Zmodem::ZPF, 0, 0, RCrc},
} ;


StateTable Zmodem::RFileOps[] = {  /* waiting for ZDATA */
    {ZDATA, &Zmodem::GotData, 0, 0, RData},    /* got it */
    {ZNAK, &Zmodem::ResendRpos, 0, 0, RFile},    /* ZRPOS was bad, resend */
    {ZEOF, &Zmodem::GotEof, 0, 0, RStart},    /* end of file */
    {ZRQINIT, &Zmodem::SendRinit, 1, 1, RStart},   /* sender confused, restart */
    {ZFILE, &Zmodem::ResendRpos, 0, 0, RFile},    /* ZRPOS was bad, resend */
    {ZFIN, &Zmodem::GotFin, 1, 1, RFinish},    /* sender signing off */
    {99, &Zmodem::ZPF, 0, 0, RFile},
} ;


/* waiting for data, but a packet could possibly arrive due
  * to error recovery or something
  */
StateTable Zmodem::RDataOps[] = {
    {ZRQINIT, &Zmodem::SendRinit, 1, 1, RStart}, /* sender confused, restart */
    {ZFILE, &Zmodem::GotFile, 0, 1, RFileName}, /* start a new file (??) */
    {ZNAK, &Zmodem::ResendRpos, 1, 1, RFile},  /* ZRPOS was bad, resend */
    {ZFIN, &Zmodem::GotFin, 1, 1, RFinish},  /* sender signing off */
    {ZDATA, &Zmodem::GotData, 0, 1, RData},  /* file data follows */
    {ZEOF, &Zmodem::GotEof, 1, 1, RStart},  /* end of file */
    {99, &Zmodem::ZPF, 0, 0, RData},
} ;

/* here if we've sent ZFERR or ZABORT.  Waiting for ZFIN */

StateTable Zmodem::RFinishOps[] = {
    {ZRQINIT, &Zmodem::SendRinit, 1, 1, RStart}, /* sender confused, restart */
    {ZFILE, &Zmodem::GotFile, 1, 1, RFileName}, /* start a new file */
    {ZNAK, &Zmodem::GotFin, 1, 1, RFinish},  /* resend ZFIN */
    {ZFIN, &Zmodem::GotFin, 1, 1, RFinish},  /* sender signing off */
    {99, &Zmodem::ZPF, 0, 0, RFinish},
} ;


/* sent ZRQINIT, waiting for response */
StateTable Zmodem::TStartOps[] = {
    {ZRINIT, &Zmodem::GotRinit, 1, 1, TStart},
    {ZCHALLENGE, &Zmodem::AnswerChallenge, 1, 0, TStart},
    {ZABORT, &Zmodem::GotAbort, 1, 1, TFinish},
    {ZFERR, &Zmodem::GotAbort, 1, 1, TFinish},
    {ZNAK, &Zmodem::Ignore, 0, 0, TStart},
    {ZCOMMAND, &Zmodem::GotCommand, 0, 0, CommandData},
    {ZSTDERR, &Zmodem::GotStderr, 0, 0, StderrData},
    {99, &Zmodem::ZPF, 0, 0, TStart},
} ;




/* sent ZSINIT, waiting for response */
StateTable Zmodem::TInitOps[] = {
    {ZACK, &Zmodem::RetDone, 1, 0, TInit},
    {ZNAK, &Zmodem::SendZSInit, 1, 0, TInit},
    {ZRINIT, &Zmodem::GotRinit, 1, 1, TInit}, /* redundant, but who cares */
    {ZCHALLENGE, &Zmodem::AnswerChallenge, 1, 0, TInit},
    {ZABORT, &Zmodem::GotAbort, 1, 1, TFinish},
    {ZFERR, &Zmodem::GotAbort, 1, 1, TFinish},
    {ZCOMMAND, &Zmodem::GotCommand, 0, 0, CommandData},
    {ZSTDERR, &Zmodem::GotStderr, 0, 0, StderrData},
    {99, &Zmodem::ZPF, 0, 0, TInit},
} ;



/* sent ZFILE, waiting for response */
StateTable Zmodem::FileWaitOps[] = {
    {ZRPOS, &Zmodem::SendFileData, 1, 1, Sending},
    {ZSKIP, &Zmodem::SkipFile, 1, 0, TStart},
    {ZCRC, &Zmodem::SendFileCrc, 1, 0, FileWait},
    {ZNAK, &Zmodem::sendFilename, 1, 0, FileWait},
    {ZRINIT, &Zmodem::sendFilename, 1, 1, FileWait}, /* rcvr confused, retry file */
    {ZABORT, &Zmodem::GotAbort, 1, 1, TFinish},
    {ZFERR, &Zmodem::GotAbort, 1, 1, TFinish},
    {ZCHALLENGE, &Zmodem::AnswerChallenge, 1, 0, FileWait},
    {ZCOMMAND, &Zmodem::GotCommand, 0, 0, CommandData},
    {ZSTDERR, &Zmodem::GotStderr, 0, 0, StderrData},
    {ZACK, &Zmodem::SendFileData, 1, 0, Sending}, // receiver always sends ZACK back
    {99, &Zmodem::ZPF, 0, 0, FileWait},
} ;


/* sent file CRC, waiting for response */
StateTable Zmodem::CrcWaitOps[] = {
    {ZRPOS, &Zmodem::SendFileData, 1, 0, Sending},
    {ZSKIP, &Zmodem::SkipFile, 1, 0, FileWait},
    {ZNAK, &Zmodem::SendFileCrc, 1, 0, CrcWait},
    {ZRINIT, &Zmodem::sendFilename, 1, 1, FileWait}, /* rcvr confused, retry file */
    {ZABORT, &Zmodem::GotAbort, 1, 1, TFinish},
    {ZFERR, &Zmodem::GotAbort, 1, 1, TFinish},
    {ZCRC, &Zmodem::SendFileCrc, 0, 0, CrcWait},
    {ZCHALLENGE, &Zmodem::AnswerChallenge, 0, 0, CrcWait},
    {ZCOMMAND, &Zmodem::GotCommand, 0, 0, CommandData},
    {ZSTDERR, &Zmodem::GotStderr, 0, 0, StderrData},
    {99, &Zmodem::ZPF, 0, 0, CrcWait},
} ;


/* sending data, interruptable */
StateTable Zmodem::SendingOps[] = {
    {ZACK, &Zmodem::GotSendAck, 0, 0, Sending},
    {ZRPOS, &Zmodem::GotSendPos, 1, 1, Sending},
    {ZSKIP, &Zmodem::SkipFile, 1, 1, FileWait},
    {ZNAK, &Zmodem::GotSendNak, 1, 1, Sending},
    {ZRINIT, &Zmodem::sendFilename, 1, 1, FileWait}, /* rcvr confused, retry file */
    {ZABORT, &Zmodem::GotAbort, 1, 1, TFinish},
    {ZFERR, &Zmodem::GotAbort, 1, 1, TFinish},
    {99, &Zmodem::ZPF, 0, 0, SendWait},
} ;

/* sent data, need to send EOF */
StateTable Zmodem::SendDoneOps[] = {
    {ZACK, &Zmodem::GotSendDoneAck, 0, 0, SendWait},
    {ZRPOS, &Zmodem::GotSendPos, 1, 1, Sending},
    {ZSKIP, &Zmodem::SkipFile, 1, 1, FileWait},
    {ZNAK, &Zmodem::GotSendNak, 1, 1, Sending},
    {ZRINIT, &Zmodem::sendFilename, 1, 1, FileWait}, /* rcvr confused, retry file */
    {ZABORT, &Zmodem::GotAbort, 1, 1, TFinish},
    {ZFERR, &Zmodem::GotAbort, 1, 1, TFinish},
    {99, &Zmodem::ZPF, 0, 0, SendWait},
} ;


/* sending data, waiting for ACK */
StateTable Zmodem::SendWaitOps[] = {
    {ZACK, &Zmodem::GotSendWaitAck, 0, 0, Sending},
    {ZRPOS, &Zmodem::GotSendPos, 0, 0, SendWait},
    {ZSKIP, &Zmodem::SkipFile, 1, 1, FileWait},
    {ZNAK, &Zmodem::GotSendNak, 0, 0, Sending},
    {ZRINIT, &Zmodem::sendFilename, 1, 1, FileWait}, /* rcvr confused, retry file */
    {ZABORT, &Zmodem::GotAbort, 1, 1, TFinish},
    {ZFERR, &Zmodem::GotAbort, 1, 1, TFinish},
    {99, &Zmodem::ZPF, 0, 0, SendWait},
} ;


/* sent ZEOF, waiting for new RINIT */
StateTable Zmodem::SendEofOps[] = {
    {ZRINIT, &Zmodem::SkipFile, 1, 0, TFinish}, /* successful completion */
    {ZACK, &Zmodem::Ignore, 0, 0, SendEof}, /* probably ACK from last packet */
    {ZRPOS, &Zmodem::GotSendPos, 1, 1, SendWait},
    {ZSKIP, &Zmodem::SkipFile, 1, 1, TStart},
    {ZNAK, &Zmodem::ResendEof, 1, 0, SendEof},
    {ZRINIT, &Zmodem::sendFilename, 1, 1, FileWait}, /* rcvr confused, retry file */
    {ZABORT, &Zmodem::GotAbort, 1, 1, TFinish},
    {ZFERR, &Zmodem::GotAbort, 1, 1, TFinish},
    {99, &Zmodem::ZPF, 0, 0, SendEof},
} ;

StateTable Zmodem::TFinishOps[] = {
    {ZFIN, &Zmodem::OverAndOut, 1, 1, Done},
    {ZNAK, &Zmodem::ZmodemTFinish, 1, 1, TFinish},
    {ZRINIT, &Zmodem::ZmodemTFinish, 1, 1, TFinish},
    {ZABORT, &Zmodem::GotAbort, 1, 1, TFinish},
    {ZFERR, &Zmodem::GotAbort, 1, 1, TFinish},
    {99, &Zmodem::ZPF, 0, 0, TFinish},
} ;



StateTable Zmodem::CommandDataOps[] = {
    {99, &Zmodem::ZPF, 0, 0, CommandData},
} ;

StateTable Zmodem::CommandWaitOps[] = {
    {99, &Zmodem::ZPF, 0, 0, CommandWait},
} ;

StateTable Zmodem::StderrDataOps[] = {
    {99, &Zmodem::ZPF, 0, 0, StderrData},
} ;

StateTable Zmodem::DoneOps[] = {
    {99, &Zmodem::ZPF, 0, 0, Done},
} ;

StateTable * Zmodem::tables[] = {
    Zmodem::RStartOps,
    Zmodem::RSinitWaitOps,
    Zmodem::RFileNameOps,
    Zmodem::RCrcOps,
    Zmodem::RFileOps,
    Zmodem::RDataOps,
    Zmodem::RDataOps, /* RDataErr is the same as RData */
    Zmodem::RFinishOps,

    Zmodem::TStartOps,
    Zmodem::TInitOps,
    Zmodem::FileWaitOps,
    Zmodem::CrcWaitOps,
    Zmodem::SendingOps,
    Zmodem::SendWaitOps,
    Zmodem::SendDoneOps,
    Zmodem::SendEofOps,
    Zmodem::TFinishOps,

    Zmodem::CommandDataOps,
    Zmodem::CommandWaitOps,
    Zmodem::StderrDataOps,
    Zmodem::DoneOps,
} ;

const char * Zmodem::hdrnames[] = {
    "ZRQINIT",
    "ZRINIT",
    "ZSINIT",
    "ZACK",
    "ZFILE",
    "ZSKIP",
    "ZNAK",
    "ZABORT",
    "ZFIN",
    "ZRPOS",
    "ZDATA",
    "ZEOF",
    "ZFERR",
    "ZCRC",
    "ZCHALLENGE",
    "ZCOMPL",
    "ZCAN",
    "ZFREECNT",
    "ZCOMMAND",
    "ZSTDERR",
} ;


#endif






Zmodem::Zmodem(QObject * parent, QObject *netinterface, QTextCodec * codec, int type)
    :QObject(parent)
{

//now set network interface Telnet

    m_codec = codec;
    connectionType = type;

// Get network interface
    if (connectionType == 0) {
        // telnet
        m_pTelnet = (Telnet *)netinterface;
    } else if (connectionType == 1) {
        //ssh
        m_pTelnet = (Telnet *)netinterface;

    }


    sending = false;

    transferstate = NoTransfer;

    zmodemTimer = new QTimer(this);
    connect(zmodemTimer, SIGNAL(timeout()), this, SLOT(ZmodemTimeout()));

#ifdef QTERM_DEBUG
    zmodemlogfile = fopen("zmodem.log", "w+");
    fprintf(zmodemlogfile, "%s", "\n================================\n");
    fclose(zmodemlogfile);
#endif


//init struct INFO
    info.zrinitflags = CANFDX | CANOVIO | CANBRK | CANFC32 ;
    info.zsinitflags = 0 ;
    info.attn = NULL ;
    info.bufsize = 0 ; /* full streaming */
    info.buffer = NULL;

    zerrno = 0;
    lastPullByte = 0;

    ZmodemReset(&info);
// other init function not complete

}

Zmodem::~Zmodem()
{
}

int Zmodem::ZmodemTInit(ZModem *info)
{
    int err ;
    int i ;

    info->state = TStart ;
    info->Protocol = ZMODEM ;
    info->crc32 = 0 ;
    info->packetCount = 0 ;
    info->errCount = 0 ;
    info->escCtrl = info->escHibit = info->atSign = info->escape = 0 ;
    info->InputState = Idle ;
    info->canCount = info->chrCount = 0 ;
    info->windowCount = 0 ;
    info->filename = NULL ;
    info->bufsize = 0 ;
    info->interrupt = 0 ;
    info->waitflag = 0 ;

// if( info->packetsize <= 0 )
    info->packetsize = 8096 ;
    info->windowsize = 0;
    /* we won't be receiving much data, pick a reasonable buffer
     * size (largest packet will do)
     */

    i = info->packetsize * 2 ;

    if (info->buffer != NULL) {
        free(info->buffer) ;
        info->buffer = NULL ;
    }         //since in the constructor function buffer is malloc

    if (i < 1024) i = 1024 ;
    info->buffer = (uchar *)malloc(i) ;

    ZIFlush(info) ;

    QString path = Global::instance()->fileCfg()->getItemValue("global", "openfiledialog").toString();
    if (strFileList.count() == 0)
        strFileList = QFileDialog::getOpenFileNames(0, "Choose the files", path, "All files(*)");
    if (strFileList.count() != 0) {
        QStringList::Iterator itFile = strFileList.begin();
        QFileInfo fi(*itFile);
        Global::instance()->fileCfg()->setItemValue("global", "openfiledialog", fi.absoluteDir().absolutePath());
        Global::instance()->fileCfg()->save();
    }

    /* optional: send "rz\r" to remote end */
    if (DoInitRZ) {
        if ((err = ZXmitStr((uchar *)"rz\r", 3, info)))
            return err ;
    }

    if ((err = ZXmitHdr(ZRQINIT, ZHEX, zeros, info)))  /* nudge receiver */
        return err ;

    info->timeout = 60 ;
    transferstate = WaitingForReply;  // transfer complete

    zmodemlog("ZmodemTInit[%s]: sent ZRQINIT\n", sname(info)) ;

    return 0 ;
}


int Zmodem::ZmodemTFile(char *file, char *rfile, uint f0, uint f1, uint f2, uint f3,
                        int filesRem, int bytesRem, ZModem *info)
{
    if (file == NULL || (info->file = fopen(file, "rb")) == NULL)
        return ZmErrCantOpen ;

    info->fileEof = 0 ;
    info->filename = file ;
    if (rfile != NULL)
        info->rfilename = rfile;
    else
        info->rfilename = strdup("noname");
    info->filesRem = filesRem ;
    info->bytesRem = bytesRem ;
    info->fileFlags[3] = f0 ;
    info->fileFlags[2] = f1 ;
    info->fileFlags[1] = f2 ;
    info->fileFlags[0] = f3 ;
    info->offset = info->lastOffset = 0 ;
    info->len = info->date = info->fileType = info->mode = 0 ;
    if (info->filename != NULL) {
        struct stat buf ;
        if (stat(info->filename, &buf) == 0) {
            info->len = buf.st_size ;
            info->date = buf.st_mtime ;
            info->fileType = 0 ;
            info->mode = (buf.st_mode & 0777) | 0100000 ;
        }
    }

    if (info->Protocol == XMODEM)
        return YSendData(info) ;

    if (info->Protocol == YMODEM)
        return YSendFilename(info) ;

    info->state = FileWait ;
    ZStatus(FileBegin, info->bytesRem, info->rfilename);
    zmodemlog("ZmodemTFile[%s]: send ZFILE(%s)\n",
              sname(info), info->rfilename) ;
    return sendFilename(info) ;
}


int Zmodem::ZmodemTFinish(ZModem *info)
{
    int i;
    if (info->Protocol == XMODEM)
        return ZmDone ;

    if (info->Protocol == YMODEM)
        return YSendFin(info) ;

    info->state = TFinish ;
    if (info->buffer != NULL) {
        free(info->buffer) ;
        info->buffer = NULL ;
    }
    zmodemlog("ZmodemTFinish[%s]: send ZFIN\n", sname(info)) ;
    i = ZXmitHdr(ZFIN, ZHEX, zeros, info) ;

    return i;
}



int Zmodem::ZmodemAbort(ZModem *info)
{
    uchar canistr[] = {
        CAN, CAN, CAN, CAN, CAN, CAN, CAN, CAN, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
    } ;
    info->state = Done ;
    ZIFlush(info) ;
    ZOFlush(info) ;

    transferstate = TransferStop;  // transfer complete
    ZmodemReset(info);  //Tranfer complete, zmodem return to receive state

    zmodemlog("ZmodemAbort[%s]: send CAN\n", sname(info));
    return ZXmitStr(canistr, sizeof(canistr), info) ;
}


int Zmodem::ZmodemRInit(ZModem *info)
{
    info->packetCount = 0 ;
    info->offset = 0 ;
    info->errCount = 0 ;
    info->escCtrl = info->escHibit = info->atSign = info->escape = 0 ;
    info->InputState = Idle ;
    info->canCount = info->chrCount = 0 ;
    info->filename = NULL ;
    info->interrupt = 0 ;
    info->waitflag = 0 ;
    info->attn = NULL ;
    info->file = NULL ;

    if (info->buffer != NULL) {
        free(info->buffer) ;
        info->buffer = NULL ;
    }

    info->buffer = (uchar *)malloc(1024) ;

    info->state = RStart ;
    info->timeoutCount = 0 ;

    ZIFlush(info) ;

    /* Don't send ZRINIT right away, there might be a ZRQINIT in
     * the input buffer.  Instead, set timeout to zero and return.
     * This will allow ZmodemRcv() to check the input stream first.
     * If nothing found, a ZRINIT will be sent immediately.
     */
    info->timeout = 0 ;

    zmodemlog("ZmodemRInit[%s]: flush input, new state = RStart\n",
              sname(info)) ;

    return 0 ;
}




int Zmodem::ZmodemRcv(uchar *str, int len, ZModem *info)
{
    register uchar c ;
    int err ;

    zmodemlog("zmodemRcv called");

    info->rcvlen = len ;

    while (--info->rcvlen >= 0) {
        c = *str++ ;

        if (c == CAN) {
            if (++info->canCount >= 5) {
                ZStatus(RmtCancel, 0, NULL) ;
                return ZmErrCancel ;
            }
        } else
            info->canCount = 0 ;

        if (info->InputState == Ysend) {
            if ((err = YsendChar(c, info)))
                return err ;
        } else if (info->InputState == Yrcv) {
            if ((err = YrcvChar(c, info)))
                return err ;
        }

        else if (c != XON && c != XOFF) {
            /* now look at what we have */
            //fprintf(stderr, "%02x, ", c);
            switch (info->InputState) {
            case Idle:
                if ((err = IdleChar(c, info)))
                    return err ;
                break ;

            case Inhdr:
                if ((err = HdrChar(c, info)))
                    return err ;
                break ;

            case Indata:
                if ((err = DataChar(c, info)))
                    return err ;
                break ;

            case Finish:
                if ((err = FinishChar(c, info)))
                    return err ;
                break ;

            default:
                break ;
            }
        }
    }

    return 0 ;
}




int Zmodem::ZmodemTimeout(/*ZModem *info*/)
{
    /* timed out while waiting for input */

    ++info.timeoutCount ;

    zmodemlog("timeout %d [%s]\n", info.timeoutCount, sname(&info)) ;

    switch (info.state) {
        /* receive */
    case RStart:  /* waiting for INIT frame from other end */
        if (info.timeoutCount > 4)
            return YmodemRInit(&info) ;

    case RSinitWait:
    case RFileName:
        if (info.timeout > 0)
            ZStatus(SndTimeout, info.timeoutCount, NULL) ;
        if (info.timeoutCount > 4)
            return ZmErrRcvTo ;
        info.state = RStart ;
        return SendRinit(&info) ;

    case RCrc:
    case RFile:
    case RData:
        ZStatus(SndTimeout, info.timeoutCount, NULL) ;
        if (info.timeoutCount > 2) {
            info.timeoutCount = 0 ;
            info.state = RStart ;
            return SendRinit(&info) ;
        }
        return info.state == RCrc ? ResendCrcReq(&info) : ResendRpos(&info) ;

    case RFinish:
        ZStatus(SndTimeout, info.timeoutCount, NULL) ;
        return ZmDone ;

    case YRStart:
    case YRDataWait:
    case YRData:
    case YREOF:
        return YrcvTimeout(&info) ;

        /* transmit */
    case TStart:  /* waiting for INIT frame from other end */
    case TInit:  /* sent INIT, waiting for ZACK */
    case FileWait: /* sent file header, waiting for ZRPOS */
    case CrcWait:  /* sent file crc, waiting for ZRPOS */
    case SendWait: /* waiting for ZACK */
    case SendEof:  /* sent EOF, waiting for ZACK */
    case TFinish:  /* sent ZFIN, waiting for ZFIN */
    case YTStart:
    case YTFile:
    case YTDataWait:
    case YTData:
    case YTEOF:
    case YTFin:
        ZStatus(RcvTimeout, 0, NULL) ;
        return ZmErrRcvTo ;

    case Sending:  /* sending data subpackets, ready for int */
        return SendMoreFileData(&info) ;

        /* general */
    case CommandData: /* waiting for command data */
    case StderrData: /* waiting for stderr data */
        return ZmErrSndTo ;
    case CommandWait: /* waiting for command to execute */
        return ZmErrCmdTo ;
    case Done:
        return ZmDone ;
    default:
        return 0 ;
    }
}



int Zmodem::ZmodemAttention(ZModem *info)
{
    /* attention received from remote end */
    if (info->state == Sending) {
        ZOFlush(info) ;
        info->interrupt = 1 ;
    }
    return 0 ;
}

int Zmodem::YmodemTInit(ZModem *info)
{
    info->state = YTStart ;
    info->Protocol = YMODEM ;
    info->errCount = 0 ;
    info->InputState = Ysend ;
    info->canCount = info->chrCount = 0 ;
    info->windowCount = 0 ;
    info->filename = NULL ;

    if (info->packetsize != 1024)
        info->packetsize = 128 ;

    info->buffer = (uchar *)malloc(1024) ;

    ZIFlush(info) ;
    ZFlowControl(0, info) ;

    info->timeout = 60 ;

    return 0 ;
}


int Zmodem::XmodemTInit(ZModem *info)
{
    (void) YmodemTInit(info) ;
    info->Protocol = XMODEM ;
    return 0 ;
}


int Zmodem::YmodemRInit(ZModem *info)
{
    info->errCount = 0 ;
    info->InputState = Yrcv ;
    info->canCount = info->chrCount = 0 ;
    info->noiseCount = 0 ;
    info->filename = NULL ;
    info->file = NULL ;

    if (info->buffer == NULL)
        info->buffer = (uchar *)malloc(1024) ;

    info->state = YRStart ;
    info->packetCount = -1 ;
    info->timeoutCount = 0 ;
    info->timeout = 10 ;
    info->offset = 0 ;

    ZIFlush(info) ;

    return ZXmitStr((uchar *)"C", 1, info) ;
}


int Zmodem::XmodemRInit(ZModem *info)
{
#if 0
    int err ;

    state = Start ;
    protocol = prot ;
    ymodem = prot == Ymodem || prot == YmodemG ;

    if (ymodem)
        strcpy(xmDefPath, file) ;
    else
        strcpy(xmFilename, file) ;

    eotCount = errorCount = errorCount2 = 0 ;

    if (err = XmodemRStart())
        return err ;

    state = Init ;
    packetId = ymodem ? 255 : 0 ;
    packetCount = 0 ;

    pktHdrLen = protocol == Xmodem ? 3 : 4 ;
#endif
    return 0 ;
}


ulong Zmodem::FileCrc(char *name)
{
    ulong crc ;
    FILE *ifile = fopen(name, "rb") ;
    int i ;

    if (ifile == NULL)  /* shouldn't happen, since we did access(2) */
        return 0 ;

    crc = 0xffffffff ;

    while ((i = fgetc(ifile)) != EOF)
        crc = UPDC32(i, crc) ;

    fclose(ifile) ;
    return ~crc ;
}



const char * Zmodem::sname(ZModem *info)
{
    return sname2(info->state) ;
}

const char * Zmodem::sname2(ZMState state)
{
    const char *names[] = {
        "RStart", "RSinitWait", "RFileName", "RCrc", "RFile", "RData",
        "RDataErr", "RFinish", "TStart", "TInit", "FileWait", "CrcWait",
        "Sending", "SendWait", "SendDone", "SendEof", "TFinish",
        "CommandData", "CommandWait", "StderrData", "Done", "YTStart",
        "YTFile", "YTDataWait", "YTData", "YTEOF", "YTFin", "YRStart",
        "YRDataWait", "YRData", "YREOF"
    } ;

    return names[(int)state] ;

}


int Zmodem::ZXmitChr(uchar c, ZModem *info)
{
    // to be completed
    return 0;
}

int Zmodem::ZXmitStr(uchar *str, int len, ZModem *info)
{
    //to be completed
    m_pTelnet->write((const char*)str, (uint)len);
    return 0;
}

void Zmodem::ZIFlush(ZModem *info)
{
    //to be completed
}

void Zmodem::ZOFlush(ZModem *info)
{
    //to be completed
}

int Zmodem::ZAttn(ZModem *info)
{
    //to be completed
    return 0;
}

void Zmodem::ZStatus(int type, int value, const char * status)
{
    QString msg = QString::fromLatin1(status);
    emit ZmodemState(type, value, status);
    switch (type) {
    case RcvByteCount:
        qDebug("received %d bytes", value);
        break;
    case SndByteCount:
        qDebug("sent %lx bytes", value);
        break;
    case RcvTimeout:
        /* receiver did not respond, aborting */
        qDebug("time out!");
        break;
    case  SndTimeout:
        /* value is # of consecutive send timeouts */
        qDebug("time out after trying %d times", value);
        break;
    case  RmtCancel:
        /* remote end has cancelled */
        qDebug("canceled by remote peer");
        break;
    case  ProtocolErr:
        /* protocol error has occurred, val=hdr */
        qDebug("unhandled header %d at state %s", value, status);
        break;
    case  RemoteMessage:
        /* message from remote end */
        qDebug("msg from remote peer: %s", status);
        break;
    case  DataErr:
        /* data error, val=error count */
        qDebug("data errors %d", value);
        break;
    case  FileErr:
        /* error writing file, val=errno */
        qDebug("falied to write file");
        break;
    case  FileBegin:
        /* file transfer begins, str=name */
        qDebug("starting file %s", status);
        break;
    case  FileEnd:
        /* file transfer ends, str=name */
        qDebug("finishing file %s", status);
        break;
    case  FileSkip:
        /* file being skipped, str=name */
        qDebug("skipping file %s", status);
        break;
    }
}

FILE * Zmodem::ZOpenFile(char *name, ulong crc, ZModem *info)
{
    //to be complete
    FILE *rval;
    int apnd = 0;
    QString str = Global::instance()->m_pref.strZmPath + m_codec->toUnicode(name);
    rval = fopen(str.toLocal8Bit(), apnd ? "ab" : "wb") ;

    if (rval == NULL)
        perror(name) ;

    return rval ;
}

int Zmodem::ZXmitHdr(int type, int format, uchar data[4], ZModem *info)
{
    if (format == ZBIN && info->crc32)
        format = ZBIN32 ;

    switch (format) {
    case ZHEX:
        return ZXmitHdrHex(type, data, info) ;

    case ZBIN:
        return ZXmitHdrBin(type, data, info) ;

    case ZBIN32:
        return ZXmitHdrBin32(type, data, info) ;

    default:
        return 0 ;
    }
}


int Zmodem::ZXmitHdrHex(int type, uchar data[4], ZModem *info)
{
    uchar buffer[128] ;
    uchar *ptr = buffer ;
    uint crc ;
    int i ;

    zmodemlog("sending  %s: %2.2x %2.2x %2.2x %2.2x = %lx\n",
              hdrnames[type], data[0], data[1], data[2], data[3],
              ZDec4(data)) ;

    *ptr++ = ZPAD ;
    *ptr++ = ZPAD ;
    *ptr++ = ZDLE ;
    *ptr++ = ZHEX ;

    ptr = putHex(ptr, type) ; crc = updcrc(type, 0) ;
    for (i = 4; --i >= 0; ++data) {
        ptr = putHex(ptr, *data) ;
        crc = updcrc(*data, crc) ;
    }
    crc = updcrc(0, crc) ; crc = updcrc(0, crc) ;
    ptr = putHex(ptr, (crc >> 8) & 0xff) ;
    ptr = putHex(ptr, crc & 0xff) ;
    *ptr++ = '\r' ;
    *ptr++ = '\n' ;
    if (type != ZACK  &&  type != ZFIN)
        *ptr++ = XON ;

    return ZXmitStr(buffer, ptr - buffer, info) ;
}


int Zmodem::ZXmitHdrBin(int type, uchar data[4], ZModem *info)
{
    uchar buffer[128] ;
    uchar *ptr = buffer ;
    uint crc ;
    int len ;

    zmodemlog("sending  %s: %2.2x %2.2x %2.2x %2.2x = %lx\n",
              hdrnames[type], data[0], data[1], data[2], data[3],
              ZDec4(data)) ;

    *ptr++ = ZPAD ;
    *ptr++ = ZDLE ;
    *ptr++ = ZBIN ;

    ptr = putZdle(ptr, type, info) ; crc = updcrc(type, 0) ;
    for (len = 4; --len >= 0; ++data) {
        ptr = putZdle(ptr, *data, info) ;
        crc = updcrc(*data, crc) ;
    }
    crc = updcrc(0, crc) ; crc = updcrc(0, crc) ;
    ptr = putZdle(ptr, (crc >> 8) & 0xff, info) ;
    ptr = putZdle(ptr, crc & 0xff, info) ;

    len = ptr - buffer ;
    return ZXmitStr(buffer, len, info) ;
}

int Zmodem::ZXmitHdrBin32(int type, uchar data[4], ZModem *info)
{
    uchar buffer[128] ;
    uchar *ptr = buffer ;
    ulong crc ;
    int len ;

    zmodemlog("sending  %s: %2.2x %2.2x %2.2x %2.2x = %lx\n",
              hdrnames[type], data[0], data[1], data[2], data[3],
              ZDec4(data)) ;

    *ptr++ = ZPAD ;
    *ptr++ = ZDLE ;
    *ptr++ = ZBIN32 ;
    ptr = putZdle(ptr, type, info) ; crc = UPDC32(type, 0xffffffffL) ;
    for (len = 4; --len >= 0; ++data) {
        ptr = putZdle(ptr, *data, info) ;
        crc = UPDC32(*data, crc) ;
    }
    crc = ~crc ;
    for (len = 4; --len >= 0; crc >>= 8)
        ptr = putZdle(ptr, crc & 0xff, info) ;

    len = ptr - buffer ;
    return ZXmitStr(buffer, len, info) ;
}


uchar * Zmodem::putZdle(uchar *ptr, uchar c, ZModem *info)
{
    uchar c2 = c & 0177 ;

    if (c == ZDLE || c2 == 020 || c2 == 021 || c2 == 023 ||
            c2 == 0177  || (c2 == 015 && connectionType == 0/*&& info->atSign*/)  ||
#ifdef COMMENT
            c2 == 035  || (c2 == '~' && info->lastCR)  ||
#endif /* COMMENT */
            c2 == 035  ||
            (c2 < 040 && info->escCtrl)) {
        *ptr++ = ZDLE ;
        if (c == 0177)
            *ptr = ZRUB0 ;
        else if (c == 0377)
            *ptr = ZRUB1 ;
        else
            *ptr = c ^ 0100 ;
    } else
        *ptr = c ;

    info->atSign = c2 == '@' ;
    info->lastCR = c2 == '\r' ;

    return ++ptr ;
}


uchar * Zmodem::ZEnc4(ulong n)
{
    static uchar buf[4] ;
    buf[0] = n & 0xff ; n >>= 8 ;
    buf[1] = n & 0xff ; n >>= 8 ;
    buf[2] = n & 0xff ; n >>= 8 ;
    buf[3] = n & 0xff ;
    return buf ;
}

ulong Zmodem::ZDec4(uchar buf[4])
{
    return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24) ;
}




int Zmodem::YrcvChar(char c, register ZModem *info)
{
    int err ;

    if (info->canCount >= 2) {
        ZStatus(RmtCancel, 0, NULL) ;
        return ZmErrCancel ;
    }

    switch (info->state) {
    case YREOF:
        if (c == EOT) {
            ZCloseFile(info) ; info->file = NULL ;
            ZStatus(FileEnd, 0, info->filename) ;
            if (info->filename != NULL)
                free(info->filename) ;
            if ((err = acceptPacket(info)) != 0)
                return err ;
            info->packetCount = -1 ;
            info->offset = 0 ;
            info->state = YRStart ;
            return ZXmitStr((uchar *)"C", 1, info) ;
        }
        /* else, drop through */

    case YRStart:
    case YRDataWait:
        switch (c) {
        case SOH:
        case STX:
            info->pktLen = c == SOH ? (128 + 4) : (1024 + 4) ;
            info->state = YRData ;
            info->chrCount = 0 ;
            info->timeout = 1 ;
            info->noiseCount = 0 ;
            info->crc = 0 ;
            break ;

        case EOT:
            /* ignore first EOT to protect against false eot */
            info->state = YREOF ;
            return rejectPacket(info) ;

        default:
            if (++info->noiseCount > 135)
                return ZXmitStr(NakStr, 1, info) ;
            break ;
        }
        break ;

    case YRData:
        info->buffer[info->chrCount++] = c ;
        if (info->chrCount >= info->pktLen)
            return ProcessPacket(info) ;
        break ;

    default:
        break ;
    }

    return 0 ;
}

int Zmodem::YrcvTimeout(ZModem *info)
{
    switch (info->state) {
    case YRStart:
        if (info->timeoutCount >= 10) {
            (void) ZXmitStr(CanStr, 2, info) ;
            return ZmErrInitTo ;
        }
        return ZXmitStr((uchar *)"C", 1, info) ;

    case YRDataWait:
    case YREOF:
    case YRData:
        if (info->timeoutCount >= 10) {
            (void) ZXmitStr(CanStr, 2, info) ;
            return ZmErrRcvTo ;
        }
        return ZXmitStr(NakStr, 1, info) ;
    default: return 0 ;
    }
}


void Zmodem::ZIdleStr(uchar *buffer, int len, ZModem *info)
{
    //to be completed
}


int Zmodem::FinishChar(char c, register ZModem *info)
{
    if (c == 'O') {
        if (++info->chrCount >= 2)
            return ZmDone ;
    } else
        info->chrCount = 0 ;
    return 0 ;
}



int Zmodem::DataChar(uchar c, register ZModem *info)
{
    if (c == ZDLE) {
        info->escape = 1 ;
        return 0 ;
    }

    if (info->escape) {
        info->escape = 0 ;
        switch (c) {
        case ZCRCE:
        case ZCRCG:
        case ZCRCQ:
        case ZCRCW:
            qDebug("Start check crc: %x", c);
            info->PacketType = c ;
            info->crcCount = (info->DataType == ZBIN32) ? 4 : 2 ;
            if (info->DataType == ZBIN)
                info->crc = updcrc(c, info->crc) ;
            else
                info->crc = UPDC32(c, info->crc) ;
            return 0 ;
        case ZRUB0: c = 0177 ; break ;
        case ZRUB1: c = 0377 ; break ;
        default: c ^= 0100 ; break ;
        }
    }
    if (connectionType == 0)
        if (lastPullByte == 0x0d && c == 0x00) {
            lastPullByte = 0;
            return 0;
        } else if (lastPullByte == 0xff && c == 0xff) {
            lastPullByte = 0;
            return 0;
        }

    lastPullByte = c;

    switch (info->DataType) {
        /* TODO: are hex data packets ever used? */

    case ZBIN:
        qDebug("we got zbin: %04x", info->crc);
        info->crc = updcrc(c, info->crc) ;
        if (info->crcCount == 0)
            info->buffer[info->chrCount++] = c ;
        else if (--info->crcCount == 0) {
            return ZDataReceived(info, (info->crc&0xffff) == 0) ;
        }
        break ;


    case ZBIN32:
        info->crc = UPDC32(c, info->crc) ;
        if (info->crcCount == 0)
            info->buffer[info->chrCount++] = c ;
        else if (--info->crcCount == 0) {
            qDebug("we got zbin32: %08x", info->crc);
            return ZDataReceived(info, info->crc == 0xdebb20e3) ;
        }
        break ;
    }
    return 0 ;
}


int Zmodem::HdrChar(uchar c, register ZModem *info)
{
    int i ;
    int crc = 0 ;

    if (c == ZDLE) {
        info->escape = 1 ;
        return 0 ;
    }

    if (info->escape) {
        info->escape = 0 ;
        switch (c) {
        case ZRUB0: c = 0177 ; break ;
        case ZRUB1: c = 0377 ; break ;
        default: c ^= 0100 ; break ;
        }
    }

    if (info->chrCount == 0) {  /* waiting for format */
        switch (c) {
        case ZHEX:
        case ZBIN:
        case ZBIN32:
            info->DataType = c ;
            info->chrCount = 1 ;
            info->crc = (info->DataType != ZBIN32) ? 0 : 0xffffffffL ;
            memset(info->hdrData, 0, sizeof(info->hdrData)) ;
            break ;
        default:
            info->InputState = Idle ;
            info->chrCount = 0 ;
            return ZXmitHdrHex(ZNAK, zeros, info) ;
        }
        return 0 ;
    }


    switch (info->DataType) {
        /* hex header is 14 hex digits, cr, lf.  Optional xon is ignored */
    case ZHEX:
        if (info->chrCount <= 14  &&   !isxdigit(c)) {
            info->InputState = Idle ;
            info->chrCount = 0 ;
            return ZXmitHdrHex(ZNAK, zeros, info) ;
        }

        if (info->chrCount <= 14) {
            i = (info->chrCount - 1) / 2 ;
            info->hdrData[i] = rcvHex(info->hdrData[i], c) ;
        }

        if (info->chrCount == 16) {
            crc = 0 ;
            for (i = 0; i < 7; ++i)
                crc = updcrc(info->hdrData[i], crc) ;
            info->InputState = Idle ;
            info->chrCount = 0 ;
            if ((crc&0xffff) != 0)
                return ZXmitHdrHex(ZNAK, zeros, info) ;
            else
                return ZProtocol(info) ;
        } else
            ++info->chrCount ;
        break ;


    case ZBIN:
        /* binary header is type, 4 bytes data, 2 bytes CRC */
        info->hdrData[info->chrCount-1] = c ;
        info->crc = updcrc(c, info->crc) ;
        if (++info->chrCount > 7) {
            info->InputState = Idle ;
            info->chrCount = 0 ;
            if ((crc&0xffff) != 0)
                return ZXmitHdrHex(ZNAK, zeros, info) ;
            else
                return ZProtocol(info) ;
        }
        break ;


    case ZBIN32:
        /* binary32 header is type, 4 bytes data, 4 bytes CRC */
        info->hdrData[info->chrCount-1] = c ;
        info->crc = UPDC32(c, info->crc) ;
        if (++info->chrCount > 9) {
            info->InputState = Idle ;
            info->chrCount = 0 ;
            if (info->crc != 0xdebb20e3)  /* see note below */
                return ZXmitHdrHex(ZNAK, zeros, info) ;
            else
                return ZProtocol(info) ;
        }
        break ;
    }
    return 0 ;
}




int Zmodem::IdleChar(uchar c, register ZModem *info)
{
    if (info->chrCount == 0) {
        if (c == ZPAD)
            ++info->chrCount ;
        else if (info->state == Sending  &&  ++info->noiseCount > MaxNoise)
            info->waitflag = 1 ;
        else if (info->state == TStart && (c == 'C' || c == 'G' || c == NAK)) {
            /* switch to ymodem */
            info->state = YTStart ;
            info->InputState = Ysend ;
            info->Protocol = YMODEM ;
            return YsendChar(c, info) ;
        } else
            ZIdleStr(&c, 1, info) ;
    }

    else {
        switch (c) {
        case ZPAD:
            ++info->chrCount ;
            break ;
        case ZDLE:
            info->InputState = Inhdr ;
            info->chrCount = 0 ;
            break ;
        default:
            while (--info->chrCount >= 0)
                ZIdleStr((uchar *)"*", 1, info) ;
            info->chrCount = 0 ;
            break ;
        }
    }
    return 0 ;
}



int Zmodem::YsendChar(char c,  ZModem *info)
{
    int err ;

    if (info->canCount >= 2) {
        ZStatus(RmtCancel, 0, NULL) ;
        return ZmErrCancel ;
    }

    switch (info->state) {
    case YTStart:  /* wait for 'G', 'C' or NAK */
        switch (c) {
        case 'G':  /* streaming YModem */
        case 'C':  /* CRC YModem */
        case NAK:  /* checksum YModem */
            info->PacketType = c ;
            return ZmDone ;
        default:
            return 0 ;
        }

    case YTFile:  /* sent filename, waiting for ACK or NAK */
        switch (c) {
        case NAK:  /* resend */
        case 'C':
        case 'G':
            ZStatus(DataErr, ++info->errCount, NULL) ;
            return YSendFilename(info) ;
        case ACK:
            info->state = YTDataWait ;
        default:
            return 0 ;
        }

    case YTDataWait: /* sent filename, waiting for G,C or NAK */
        switch (c) {
        case NAK:
        case 'C':
        case 'G':
            info->chrCount = 0 ;
            if (info->PacketType == 'G') { /* send it all at once */
                while (info->state == YTData)
                    if ((err = YSendData(info)))
                        return err ;
                return 0 ;
            } else
                return YSendData(info) ;
        default:
            return 0 ;
        }

    case YTData:  /* sent data, waiting for ACK or NAK */
        switch (c) {
        case 'C':
        case 'G':  /* protocol failure, resend filename */
            if (info->Protocol == YMODEM) {
                ZStatus(DataErr, ++info->errCount, NULL) ;
                info->state = YTFile ;
                rewind(info->file) ;
                return YSendFilename(info) ;
            }
            /* else XModem, treat it like a NAK */
        case NAK:
            ZStatus(DataErr, ++info->errCount, NULL) ;
            return YXmitData(info->buffer + info->bufp, info->ylen, info) ;
        case ACK:
            info->offset += info->ylen ;
            info->bufp += info->ylen ;
            info->chrCount -= info->ylen ;
            ZStatus(SndByteCount, info->offset, NULL) ;
            return YSendData(info) ;
        default:
            return 0 ;
        }

    case YTEOF:  /* sent EOF, waiting for ACK or NAK */
        switch (c) {
        case NAK:
            return ZXmitStr(eotstr, 1, info) ;
        case ACK:
            info->state = info->Protocol == YMODEM ? YTStart : Done ;
            return ZmDone ;
        default:
            return 0 ;
        }

    case YTFin:  /* sent Fin, waiting for ACK or NAK */
        switch (c) {
        case NAK:
            return YSendFin(info) ;
        case ACK:
            return ZmDone ;
        default:
            return 0 ;
        }
    default:
        return 0 ;
    }
}



int Zmodem::ZProtocol(register ZModem *info)
{
    register StateTable *table ;

    zmodemlog("received %s: %2.2x %2.2x %2.2x %2.2x = %lx\n",
              hdrnames[info->hdrData[0]], info->hdrData[1],
              info->hdrData[2], info->hdrData[3], info->hdrData[4],
              ZDec4(info->hdrData + 1)) ;

    /* Flags are sent in F3 F2 F1 F0 order.  Data is sent in P0 P1 P2 P3 */

    info->timeoutCount = 0 ;
    info->noiseCount = 0 ;

// zmodemTimer->start(info->timeout*1000);

    table = tables[(int)info->state] ;
    while (table->type != 99  &&  table->type != info->hdrData[0])
        ++table ;

    zmodemlog("  state %s => %s, iflush=%d, oflush=%d, call %x\n",
              sname(info), sname2(table->newstate), table->IFlush,
              table->OFlush, table->func) ;

    info->state = table->newstate ;

    if (table->IFlush) {
        info->rcvlen = 0 ; ZIFlush(info) ;
    }
    if (table->OFlush) ZOFlush(info) ;
    return (this->*(table->func))(info) ;
}


int Zmodem::ZDataReceived(register ZModem *info, int crcGood)
{
    switch (info->state) {
    case RSinitWait: return GotSinitData(info, crcGood) ;
    case RFileName: return GotFileName(info, crcGood) ;
    case RData: return GotFileData(info, crcGood) ;
    case CommandData: return GotCommandData(info, crcGood) ;
    case StderrData: return GotStderrData(info, crcGood) ;
    default: return ZPF(info) ;
    }
}


int Zmodem::ZPF(ZModem *info)
{
    info->waitflag = 1 ; /* pause any in-progress transmission */
    ZStatus(ProtocolErr, info->hdrData[0], sname(info)) ;
    return 0 ;
}


int Zmodem::Ignore(ZModem *info)
{
    return 0 ;
}


int Zmodem::AnswerChallenge(register ZModem *info)
{
    return ZXmitHdrHex(ZACK, info->hdrData + 1, info) ;
}


int Zmodem::GotAbort(register ZModem *info)
{
    ZStatus(RmtCancel, 0, NULL) ;
    return ZXmitHdrHex(ZFIN, zeros, info) ;
}


int Zmodem::GotCancel(ZModem *info)
{
    return ZmErrCancel ;
}


int Zmodem::GotCommand(ZModem *info)
{
    uchar rbuf[4] ;
    /* TODO: add command capability */


    /// EPERM not defined????????

    rbuf[0] = EPERM ;
    rbuf[1] = rbuf[2] = rbuf[3] = 0 ;
    return ZXmitHdrHex(ZCOMPL, rbuf, info) ;
}

int Zmodem::GotStderr(register ZModem *info)
{
    info->InputState = Indata ;
    info->chrCount = 0 ;
    return 0 ;
}


int Zmodem::RetDone(ZModem *info)
{


    return ZmDone ;
}


int Zmodem::GotCommandData(register ZModem *info , int crcGood)
{
    /* TODO */
    return 0 ;
}


int Zmodem::GotStderrData(register ZModem *info, int crcGood)
{
    info->buffer[info->chrCount] = '\0' ;
    ZStatus(RemoteMessage, info->chrCount, (char *)info->buffer) ;
    return 0 ;
}


int Zmodem::GotFileName(ZModem *info, int crcGood)
{
    info->InputState = Idle ;
    info->chrCount = 0 ;

    if (!crcGood) {
        zmodemlog("GotFileName[%s]: bad crc, send ZNAK\n", sname(info)) ;
        info->state = RStart ;
        return ZXmitHdrHex(ZNAK, zeros, info) ;
    }

    parseFileName(info, (char *)info->buffer) ;

    if ((info->f1 & ZMMASK) == ZMCRC) {
        info->state = RCrc ;
        return ZXmitHdrHex(ZCRC, zeros, info) ;
    }

    zmodemlog("GotFileName[%s]: good crc, call requestFile\n",
              sname(info)) ;
    info->state = RFile ;
    return requestFile(info, 0) ;
}


int Zmodem::ResendCrcReq(ZModem *info)
{
    zmodemlog("ResendCrcReq[%s]: send ZCRC\n", sname(info)) ;
    return ZXmitHdrHex(ZCRC, zeros, info) ;
}


int Zmodem::GotSinitData(ZModem *info, int crcGood)
{
    info->InputState = Idle ;
    info->chrCount = 0 ;
    info->state = RStart ;

    zmodemlog("GotSinitData[%s]: crcGood=%d\n", sname(info), crcGood) ;

    if (!crcGood)
        return ZXmitHdrHex(ZNAK, zeros, info) ;

    if (info->attn != NULL)
        free(info->attn) ;
    info->attn = NULL ;
    if (info->buffer[0] != '\0')
        info->attn = strdup((char *)info->buffer) ;
    return ZXmitHdrHex(ZACK, ZEnc4(SerialNo), info) ;
}


int Zmodem::ResendRpos(ZModem *info)
{
    zmodemlog("ResendRpos[%s]: send ZRPOS(%ld)\n",
              sname(info), info->offset) ;
    return ZXmitHdrHex(ZRPOS, ZEnc4(info->offset), info) ;
}


int Zmodem::GotFileData(ZModem *info, int crcGood)
{
    /* OK, now what?  Fushing the buffers and executing the
     * attn sequence has likely chopped off the input stream
     * mid-packet.  Now we switch to idle mode and treat all
     * incoming stuff like noise until we get a new valid
     * packet.
     */

    if (!crcGood) {   /* oh bugger, an error. */
        zmodemlog(
            "GotFileData[%s]: bad crc, send ZRPOS(%ld), new state = RFile\n",
            sname(info), info->offset) ;
        ZStatus(DataErr, ++info->errCount, NULL) ;
        if (info->errCount > MaxErrs) {
            ZmodemAbort(info) ;
            return ZmDataErr ;
        } else {
            info->state = RFile ;
            info->InputState = Idle ;
            info->chrCount = 0 ;
            return fileError(info, ZRPOS, info->offset) ;
        }
    }

    if (ZWriteFile(info->buffer, info->chrCount, info->file, info)) {
        /* RED ALERT!  Could not write the file. */
        ZStatus(FileErr, zerrno, NULL) ;
        info->state = RFinish ;
        info->InputState = Idle ;
        info->chrCount = 0 ;
        return fileError(info, ZFERR, zerrno) ;
    }

    zmodemlog("GotFileData[%s]: %ld.%d,",
              sname(info), info->offset, info->chrCount) ;
    info->offset += info->chrCount ;
    ZStatus(RcvByteCount, info->offset, NULL) ;

    /* if this was the last data subpacket, leave data mode */
    if (info->PacketType == ZCRCE  ||  info->PacketType == ZCRCW) {
        zmodemlog("  ZCRCE|ZCRCW, new state RFile") ;
        info->state = RFile ;
        info->InputState = Idle ;
        info->chrCount = 0 ;
    } else {
        zmodemlog("  call dataSetup") ;
        (void) dataSetup(info) ;
    }

    if (info->PacketType == ZCRCQ || info->PacketType == ZCRCW) {
        zmodemlog(",  send ZACK\n") ;
        return ZXmitHdrHex(ZACK, ZEnc4(info->offset), info) ;
    } else
        zmodemlog("\n") ;

    return 0 ;
}

int Zmodem::SendRinit(ZModem *info)
{
    uchar dbuf[4] ;



#ifdef COMMENT
    if (info->timeoutCount >= 5)
        /* TODO: switch to Ymodem */
#endif /* COMMENT */

        transferstate = TransferStart; //transfer would be active, it must be set to false when transfer complete or abort
    zmodemlog("SendRinit[%s]: send ZRINIT\n", sname(info)) ;

    info->timeout = ResponseTime ;
    dbuf[0] = info->bufsize & 0xff ;
    dbuf[1] = (info->bufsize >> 8) & 0xff ;
    dbuf[2] = 0 ;
    dbuf[3] = info->zrinitflags ;
    return ZXmitHdrHex(ZRINIT, dbuf, info) ;
}


int Zmodem::SendMoreFileData(ZModem *info)
{
    int type ;
    int qfull = 0 ;
    int err ;
    int len ;  /* max # chars to send this packet */
    long pending ; /* # of characters sent but not acknowledged */

    /* ZCRCE: CRC next, frame ends, header follows
     * ZCRCG: CRC next, frame continues nonstop
     * ZCRCQ: CRC next, send ZACK, frame continues nonstop
     * ZCRCW: CRC next, send ZACK, frame ends, header follows
     */

    if (info->interrupt) {
        /* Bugger, receiver sent an interrupt.  Enter a wait state
         * and see what they want.  Next header *should* be ZRPOS.
         */
        info->state = SendWait ;
        info->timeout = 60 ;
        return 0 ;
    }

    /* Find out how many bytes we can transfer in the next packet */

    len = info->packetsize ;

    pending = info->offset - info->lastOffset ;

    if (info->windowsize != 0 && info->windowsize - pending <= len) {
        len = info->windowsize - pending ;
        qfull = 1 ;
    }
    if (info->bufsize != 0  &&  info->bufsize - pending <= len) {
        len = info->bufsize - pending ;
        qfull = 1 ;
    }

    if (len == 0) {  /* window still full, keep waiting */
        info->state = SendWait ;
        info->timeout = 60 ;
        return 0 ;
    }


    /* OK, we can safely transmit 'len' bytes of data.  Start reading
     * file until buffer is full.
     */

    len -= 10 ; /* Pre-deduct 10 bytes for trailing CRC */


    /* find out what kind of packet to send */
    if (info->waitflag) {
        type = ZCRCW ;
        info->waitflag = 0 ;
    }
#ifdef COMMENT
    else if (info->fileEof)
        type = ZCRCE ;
#endif /* COMMENT */
    else if (qfull)
        type = ZCRCW ;
    else
        switch (info->Streaming) {
        case Full:
        case Segmented: type = ZCRCG ; break ;

        case StrWindow:
            if ((info->windowCount += len) < info->windowsize / 4)
                type = ZCRCG ;
            else {
                type = ZCRCQ ;
                info->windowCount = 0 ;
            }
            break ;

        default:
        case SlidingWindow: type = ZCRCQ ; break ;
        }


    {
        int crc32 = info->crc32 ;
        int c = 0, c2, atSign = 0 ;
        ulong crc ;
        uchar *ptr = info->buffer ;

        crc = crc32 ? 0xffffffff : 0 ;

        /* read characters from file and put into buffer until buffer is
         * full or file is exhausted
         */


        while (len > 0  && (c = getc(info->file)) != EOF) {
            if (!crc32)
                crc = updcrc(c, crc) ;
            else
                crc = UPDC32(c, crc) ;

            /* zmodem protocol requires that CAN(ZDLE), DLE, XON, XOFF and
             * a CR following '@' be escaped.  In addition, I escape '^]'
             * to protect telnet, "<CR>~." to protect rlogin, and ESC for good
             * measure.
             */
            c2 = c & 0177 ;
            if (c == ZDLE || c2 == 020 || c2 == 021 || c2 == 023 ||
                    c2 == 0177  ||  c2 == '\r'  ||  c2 == '\n'  ||  c2 == 033  ||
                    c2 == 035  || (c2 < 040 && info->escCtrl)) {
                *ptr++ = ZDLE ;
                if (c == 0177)
                    *ptr = ZRUB0 ;
                else if (c == 0377)
                    *ptr = ZRUB1 ;
                else
                    *ptr = c ^ 0100 ;
                len -= 2 ;
            } else {
                *ptr = c ;
                --len ;
            }
            ++ptr ;

            atSign = c2 == '@' ;
            ++info->offset ;
        }

        /* if we've reached file end, a ZEOF header will follow.  If
         * there's room in the outgoing buffer for it, end the packet
         * with ZCRCE and append the ZEOF header.  If there isn't room,
         * we'll have to do a ZCRCW
         */
        if ((info->fileEof = (c == EOF))) {
            if (qfull  || (info->bufsize != 0 && len < 24))
                type = ZCRCW ;
            else
                type = ZCRCE ;
        }

        *ptr++ = ZDLE ;
        if (!crc32)
            crc = updcrc(type, crc) ;
        else
            crc = UPDC32(type, crc) ;
        *ptr++ = type ;

        if (!crc32) {
            crc = updcrc(0, crc) ; crc = updcrc(0, crc) ;
            ptr = putZdle(ptr, (crc >> 8) & 0xff, info) ;
            ptr = putZdle(ptr, crc & 0xff, info) ;
        } else {
            crc = ~crc ;
            for (len = 4; --len >= 0; crc >>= 8)
                ptr = putZdle(ptr, crc & 0xff, info) ;
        }

        len = ptr - info->buffer ;
    }

    ZStatus(SndByteCount, info->offset, NULL) ;

    if ((err = ZXmitStr(info->buffer, len, info)))
        return err ;

#ifdef COMMENT
    if ((err = ZXmitData(ZBIN, len, uchar(type), info->buffer, info)))
        return err ;
#endif /* COMMENT */

    /* finally, do we want to wait after this packet? */

    switch (type) {
    case ZCRCE:
        info->state = SendEof ;
        info->timeout = 60 ;
        return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info) ;
    case ZCRCW:
        info->state = info->fileEof ? SendDone : SendWait ;
        info->timeout = 60 ;
        break ;
    default:
        info->state = Sending ;
        info->timeout = 0 ;
        break ;
    }


#ifdef COMMENT
    if (info->fileEof) {  /* Yes, file is done, send EOF and wait */
        info->state = SendEof ;
        info->timeout = 60 ;
        return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info) ;
    } else if (type == ZCRCW) {
        info->state = SendWait ;
        info->timeout = 60 ;
    } else {
        info->state = Sending ;
        info->timeout = 0 ;
    }
#endif /* COMMENT */
    return 0 ;
}


uint Zmodem::rcvHex(uint i, char c)
{
    if (c <= '9')
        c -= '0' ;
    else if (c <= 'F')
        c -= 'A' -10 ;
    else
        c -= 'a' -10 ;
    return (i << 4) + c ;
}


int Zmodem::dataSetup(register ZModem *info)
{
    info->InputState = Indata ;
    info->chrCount = 0 ;
    info->crcCount = 0 ;
    info->crc = (info->DataType != ZBIN32) ? 0 : 0xffffffffL ;
    return 0 ;
}

int Zmodem::ZWriteFile(uchar *buffer, int len, FILE *file, ZModem *)
{

    return fwrite(buffer, 1, len, file) == len ? 0 : ZmErrSys ;;
}

int Zmodem::ZCloseFile(ZModem *info)
{
    //to be completed
    fclose(info->file) ;
    return 0;
}

void Zmodem::ZFlowControl(int onoff, ZModem *info)
{
    //to be completed
}

int Zmodem::GotSinit(ZModem *info)
{
    zmodemlog("GotSinit[%s]: call dataSetup\n", sname(info)) ;

    info->zsinitflags = info->hdrData[4] ;
    info->escCtrl = info->zsinitflags & TESCCTL ;
    info->escHibit = info->zsinitflags & TESC8 ;
    ZFlowControl(1, info) ;
    return dataSetup(info) ;
}

int Zmodem::GotFile(ZModem *info)
{
    zmodemlog("GotFile[%s]: call dataSetup\n", sname(info)) ;

    info->errCount = 0 ;
    info->f0 = info->hdrData[4] ;
    info->f1 = info->hdrData[3] ;
    info->f2 = info->hdrData[2] ;
    info->f3 = info->hdrData[1] ;
    return dataSetup(info) ;
}

int Zmodem::GotFin(ZModem *info)
{
    int i;
    zmodemlog("GotFin[%s]: send ZFIN\n", sname(info)) ;
    info->InputState = Finish ;
    info->chrCount = 0 ;
    if (info->filename != NULL)
        free(info->filename) ;
    i = ZXmitHdrHex(ZFIN, zeros, info) ;
    ZmodemReset(info);
    transferstate = TransferStop;  // transfer complete
    return i;
}


int Zmodem::GotData(ZModem *info)
{
    int err ;

    zmodemlog("GotData[%s]:\n", sname(info)) ;

    if (ZDec4(info->hdrData + 1) != info->offset) {
        if (info->attn != NULL  && (err = ZAttn(info)) != 0)
            return err ;
        zmodemlog("  bad, send ZRPOS(%ld)\n", info->offset);
        return ZXmitHdrHex(ZRPOS, ZEnc4(info->offset), info) ;
    }

    /* Let's do it! */
    zmodemlog("  call dataSetup\n");
    return dataSetup(info) ;
}

int Zmodem::GotEof(ZModem *info)
{
    zmodemlog("GotEof[%s]: offset=%ld\n", sname(info), info->offset) ;
    if (ZDec4(info->hdrData + 1) != info->offset) {
        zmodemlog("zdec4(info->hdrdata+1)=%ld\n", ZDec4(info->hdrData + 1));
        zmodemlog("  bad length, state = RFile\n") ;
        info->state = RFile ;
        return 0 ;  /* it was probably spurious */
    }

    /* TODO: if we can't close the file, send a ZFERR */

    ZCloseFile(info) ; info->file = NULL ;
    ZStatus(FileEnd, 0, info->filename) ;
    if (info->filename != NULL) {
        free(info->filename) ;
        info->filename = NULL ;
    }
    return SendRinit(info) ;
}

int Zmodem::GotFreecnt(ZModem *info)
{
    /* TODO: how do we find free space on system? */
    return ZXmitHdrHex(ZACK, ZEnc4(0xffffffff), info) ;
}


int Zmodem::GotFileCrc(ZModem *info)
{
    zmodemlog("GotFileCrc[%s]: call requestFile\n", sname(info)) ;
    return requestFile(info, ZDec4(info->hdrData + 1)) ;
}

int Zmodem::requestFile(ZModem *info, ulong crc)
{
    info->file = ZOpenFile((char *)info->buffer, crc, info) ;

    if (info->file == NULL) {
        zmodemlog("requestFile[%s]: send ZSKIP\n", sname(info)) ;

        info->state = RStart ;
        ZStatus(FileSkip, 0, info->filename) ;
        return ZXmitHdrHex(ZSKIP, zeros, info) ;
    } else {
        zmodemlog("requestFile[%s]: send ZRPOS(%ld)\n",
                  sname(info), info->offset) ;
        info->offset = info->f0 == ZCRESUM ? ftell(info->file) : 0 ;
        info->state = RFile ;
        ZStatus(FileBegin, info->len, info->filename) ;
        return ZXmitHdrHex(ZRPOS, ZEnc4(info->offset), info) ;
    }
}

void Zmodem::parseFileName(ZModem *info, char *fileinfo)
{
    char *ptr ;
    int serial = 0 ;

    info->len = info->mode = info->filesRem =
                                 info->bytesRem = info->fileType = 0 ;
    ptr = fileinfo + strlen(fileinfo) + 1 ;
    if (info->filename != NULL)
        free(info->filename) ;
    info->filename = strdup(fileinfo) ;
    sscanf(ptr, "%d %lo %o %o %d %d %d", &info->len, &info->date,
           &info->mode, &serial, &info->filesRem, &info->bytesRem,
           &info->fileType) ;
}



int Zmodem::fileError(ZModem *info, int type, int data)
{
    int err ;

    info->InputState = Idle ;
    info->chrCount = 0 ;

    if (info->attn != NULL  && (err = ZAttn(info)) != 0)
        return err ;
    return ZXmitHdrHex(type, ZEnc4(data), info) ;
}


int Zmodem::ProcessPacket(ZModem *info)
{
    int idx = (uchar) info->buffer[0] ;
    int idxc = (uchar) info->buffer[1] ;
    int crc0, crc1 ;
    int err ;

    info->state = YRDataWait ;

    if (idxc != 255 - idx) {
        ZStatus(DataErr, ++info->errCount, NULL) ;
        return rejectPacket(info) ;
    }

    if (idx == (info->packetCount % 256)) /* quietly ignore dup */
        return acceptPacket(info) ;

    if (idx != (info->packetCount + 1) % 256) { /* out of sequence */
        (void) ZXmitStr(CanStr, 2, info) ;
        return ZmErrSequence ;
    }

    crc0 = (uchar)info->buffer[info->pktLen-2] << 8 |
           (uchar)info->buffer[info->pktLen-1] ;
    crc1 = calcCrc(info->buffer + 2, info->pktLen - 4) ;
    if (crc0 != crc1) {
        ZStatus(DataErr, ++info->errCount, NULL) ;
        return rejectPacket(info) ;
    }

    ++info->packetCount ;

    if (info->packetCount == 0) { /* packet 0 is filename */
        if (info->buffer[2] == '\0') {  /* null filename is FIN */
            (void) acceptPacket(info) ;
            return ZmDone ;
        }

        parseFileName(info, (char *)info->buffer + 2) ;
        info->file = ZOpenFile(info->filename, 0, info) ;
        if (info->file == NULL) {
            (void) ZXmitStr(CanStr, 2, info) ;
            return ZmErrCantOpen ;
        }
        if ((err = acceptPacket(info)) != 0)
            return err ;
        return ZXmitStr((uchar *)"C", 1, info) ;
    }


    if (ZWriteFile(info->buffer + 2, info->pktLen - 4, info->file, info)) {
        ZStatus(FileErr, zerrno, NULL) ;
        (void) ZXmitStr(CanStr, 2, info) ;
        return ZmErrSys ;
    }
    info->offset += info->pktLen - 4 ;
    ZStatus(RcvByteCount, info->offset, NULL) ;

    (void) acceptPacket(info) ;
    return 0 ;
}

int Zmodem::rejectPacket(ZModem *info)
{
    info->timeout = 10 ;
    return ZXmitStr(NakStr, 1, info) ;
}


int Zmodem::acceptPacket(ZModem *info)
{
    info->state = YRDataWait ;
    info->timeout = 10 ;
    return ZXmitStr(AckStr, 1, info) ;
}


int Zmodem::calcCrc(uchar *str, int len)
{
    int crc = 0 ;
    while (--len >= 0)
        crc = updcrc(*str++, crc) ;
    crc = updcrc(0, crc) ; crc = updcrc(0, crc) ;
    return crc & 0xffff ;
}


char * Zmodem::strdup(const char *str)
{
    char *rval ;
    int len = strlen(str) + 1 ;
    rval = (char *)malloc(len) ;
    strcpy(rval, str) ;
    return rval ;
}


int Zmodem::ZXmitData(int format, int len, uchar term, uchar *data, ZModem *info)
{
    uchar *ptr = info->buffer ;
    uint crc ;

    if (format == ZBIN && info->crc32)
        format = ZBIN32 ;

    zmodemlog("ZXmiteData: fmt=%c, len=%d, term=%c\n", format, len, term) ;

    crc = (format == ZBIN) ? 0 : 0xffffffff ;

    while (--len >= 0) {
        if (format == ZBIN)
            crc = updcrc(*data, crc) ;
        else
            crc = UPDC32(*data, crc) ;
        ptr = putZdle(ptr, *data++, info) ;
    }

    *ptr++ = ZDLE ;
    if (format == ZBIN)
        crc = updcrc(term, crc) ;
    else
        crc = UPDC32(term, crc) ;
    *ptr++ = term ;
    if (format == ZBIN) {
        crc = updcrc(0, crc) ; crc = updcrc(0, crc) ;
        ptr = putZdle(ptr, (crc >> 8) & 0xff, info) ;
        ptr = putZdle(ptr, crc & 0xff, info) ;
    } else {
        crc = ~crc ;
        for (len = 4; --len >= 0; crc >>= 8)
            ptr = putZdle(ptr, crc & 0xff, info) ;
    }

    return ZXmitStr(info->buffer, ptr - info->buffer, info) ;
}



int Zmodem::YXmitData(uchar *buffer, int len, ZModem *info)
{
    uchar hdr[3] ;
    uchar trail[2] ;
    ulong crc = 0 ;
    int i, err ;

    hdr[0] = len == 1024 ? STX : SOH ;
    hdr[1] = info->packetCount ;
    hdr[2] = ~hdr[1] ;
    if ((err = ZXmitStr(hdr, 3, info)) ||
            (err = ZXmitStr(buffer, len, info)))
        return err ;

    if (info->PacketType == NAK) {  /* checksum */
        for (i = 0; i < len; ++i)
            crc += buffer[i] ;
        trail[0] = crc % 256 ;
        return ZXmitStr(trail, 1, info) ;
    } else {
        for (i = 0; i < len; ++i)
            crc = updcrc(buffer[i], crc) ;
        crc = updcrc(0, crc) ; crc = updcrc(0, crc) ;
        trail[0] = crc / 256 ;
        trail[1] = crc % 256 ;
        return ZXmitStr(trail, 2, info) ;
    }
}


int Zmodem::YSendFilename(ZModem *info)
{
    int i, len ;
    uchar obuf[1024] ;
    uchar *ptr = obuf ;

    info->state = info->PacketType != 'G' ? YTFile : YTDataWait ;
    info->packetCount = 0 ;
    info->offset = 0 ;

    i = strlen(info->rfilename) ;
    memcpy(ptr, info->rfilename, i + 1) ;  ptr += i + 1 ;
    sprintf((char *)ptr, "%d %lo %o 0", info->len, info->date, info->mode);
    ptr += strlen((char *)ptr) ;
    *ptr++ = '\0' ;
    /* pad out to 128 bytes or 1024 bytes */
    i = ptr - obuf ;
    len = i > 128 ? 1024 : 128 ;
    for (; i < len; ++i)
        *ptr++ = '\0' ;

    return YXmitData(obuf, len, info) ;
}

int Zmodem::YSendData(ZModem *info)
{
    int i ;

    /* are there characters still in the read buffer? */

    if (info->chrCount <= 0) {
        info->bufp = 0 ;
        info->chrCount = fread(info->buffer, 1, info->packetsize, info->file);
        info->fileEof = feof(info->file) ;
    }

    if (info->chrCount <= 0) {
        fclose(info->file) ;
        info->state = YTEOF ;
        return ZXmitStr(eotstr, 1, info) ;
    }

    /* pad out to 128 bytes if needed */
    if (info->chrCount < 128) {
        i = 128 - info->chrCount ;
        memset(info->buffer + info->bufp + info->chrCount, 0x1a, i) ;
        info->chrCount = 128 ;
    }

    info->ylen = info->chrCount >= 1024 ? 1024 : 128 ;
    ++info->packetCount ;

    info->state = YTData ;

    return YXmitData(info->buffer + info->bufp, info->ylen, info) ;
}


int Zmodem::YSendFin(ZModem *info)
{
    uchar obuf[128] ;

    info->state = YTFin ;
    info->packetCount = 0 ;

    memset(obuf, 0, 128) ;

    return YXmitData(obuf, 128, info) ;
}

int Zmodem::sendFilename(ZModem *info)
{
    int err ;
    int i ;
    uchar obuf[2048] ;
    uchar *ptr = obuf ;

    info->state = FileWait ;

    if ((err = ZXmitHdr(ZFILE, ZBIN, info->fileFlags, info)))
        return err ;

    i = strlen(info->rfilename) ;
    memcpy(ptr, info->rfilename, i + 1) ;  ptr += i + 1 ;
    sprintf((char *)ptr, "%d %lo %o 0 %d %d 0", info->len, info->date,
            info->mode, info->filesRem, info->bytesRem) ;
    ptr += strlen((char *)ptr) ;
    *ptr++ = '\0' ;

    return ZXmitData(ZBIN, ptr - obuf, ZCRCW, obuf, info) ;
}

int Zmodem::GotRinit(ZModem *info)
{
    if (strFileList.count() == 0)
        return ZmodemTFinish(info);


    info->bufsize = info->hdrData[1] + info->hdrData[2] * 256 ;
    info->zrinitflags = info->hdrData[4] + info->hdrData[3] * 256 ;
    info->crc32 = info->zrinitflags & CANFC32 ;
    info->escCtrl = info->zrinitflags & ESCCTL ;
    info->escHibit = info->zrinitflags & ESC8 ;

    /* Full streaming: If receiver can overlap I/O, and if
     * the sender can sample the reverse channel without hanging,
     * and the receiver has not specified a buffer size, then we
     * can simply blast away with ZCRCG packets.  If the receiver
     * detects an error, it sends an attn sequence and a new ZRPOS
     * header to restart the file where the error occurred.
     *
     * [note that zmodem8.doc does not define how much noise is
     * required to trigger a ZCRCW packet.  We arbitrarily choose
     * 64 bytes]
     *
     * If 'windowsize' is nonzero, and the receiver can do full
     * duplex, ZCRCQ packets are sent instead of ZCRCG, to keep track
     * of the number of characters in the queue.  If the queue fills
     * up, we pause and wait for a ZACK.
     *
     *
     * Full Streaming with Reverse Interrupt:  If sender cannot
     * sample the input stream, then we define an Attn sequence
     * that will be used to interrupt transmission.
     *
     *
     * Full Streaming with Sliding Window:  If sender cannot
     * sample input stream or respond to Attn signal, we send
     * several ZCRCQ packets until we're sure the receiver must
     * have sent back at least one ZACK.  Then we stop sending and
     * read that ZACK.  Then we send one more packet and so on.
     *
     *
     * Segmented Streaming:  If receiver cannot overlap I/O or can't do
     * full duplex and has specified a maximum receive buffer size,
     * whenever the buffer size is reached, we send a ZCRCW packet.
     *
     * TODO: what if receiver can't overlap, but hasn't set a buffer
     * size?
     *
     * ZCRCE: CRC next, frame ends, header follows
     * ZCRCG: CRC next, frame continues nonstop
     * ZCRCQ: CRC next, send ZACK, frame continues nonstop
     * ZCRCW: CRC next, send ZACK, frame ends, header follows
     */

    transferstate = TransferStart;

    ZFlowControl(1, info) ;

    if ((info->zrinitflags & (CANFDX | CANOVIO)) == (CANFDX | CANOVIO) &&
            (SendSample || SendAttn)  &&
            info->bufsize == 0) {
        if (info->windowsize == 0) {
            info->Streaming = Full ;
        } else {
            info->Streaming = StrWindow ;
        }
    }

    else if ((info->zrinitflags & (CANFDX | CANOVIO)) == (CANFDX | CANOVIO) &&
             info->bufsize == 0) {
        info->Streaming = SlidingWindow ;
    }

    else {
        info->Streaming = Segmented ;
    }
    // get filenames to transfer
    zmodemlog("GotRinit[%s]\n", sname(info)) ;

    if (AlwaysSinit || info->zsinitflags != 0  ||  info->attn != NULL)
        SendZSInit(info) ;


    itFile = strFileList.begin();
    QFileInfo fi(*itFile);
    qDebug("files to be transferred %d", strFileList.count());
    char *filename = strdup(fi.absoluteFilePath().toLatin1());
    char *rfilename = strdup(fi.fileName().toLatin1());
    ZmodemTFile(filename, rfilename,
                0, 0, 0, 0, strFileList.count(), fi.size(), info);
    strFileList.erase(itFile);
    return ZmDone;
}


int Zmodem::SendZSInit(ZModem *info)
{
    int err ;
    //char *at = (info->attn != NULL) ? info->attn : "" ;
    char * at;
    if (info->attn != NULL)
        at = info->attn;
    else
        at = "";
    uchar fbuf[4] ;

    /* TODO: zmodem8.doc states: "If the ZSINIT header specifies
     * ESCCTL or ESC8, a HEX header is used, and the receiver
     * activates the specified ESC modes before reading the following
     * data subpacket." What does that mean?
     */

    zmodemlog("SendZSInit[%s]\n", sname(info)) ;

    info->state = TInit ;
    fbuf[0] = fbuf[1] = fbuf[2] = 0 ;
    fbuf[3] = info->zsinitflags ;
    if ((err = ZXmitHdr(ZSINIT, ZBIN, fbuf, info))  ||
            (err = ZXmitData(ZBIN, strlen(at) + 1, ZCRCW, (uchar *)at, info))) {
        return err ;
    }
    return 0 ;
}


int Zmodem::SendFileCrc(ZModem *info)
{
    ulong crc ;

    crc = FileCrc(info->filename) ;

    zmodemlog("SendFileCrc[%s]: %lx\n", sname(info), crc) ;

    return ZXmitHdrHex(ZCRC, ZEnc4(crc), info) ;
}

int Zmodem::GotSendAck(ZModem *info)
{
    ulong offset ;

    offset = ZDec4(info->hdrData + 1) ;

    if (offset > info->lastOffset)
        info->lastOffset = offset ;

    zmodemlog("GotSendAck[%s]: %lx\n", sname(info), info->offset) ;

    return 0 ;  /* DONT send more data, that will happen
     * later anyway */
}

int Zmodem::GotSendDoneAck(ZModem *info)
{
    ulong offset ;

    offset = ZDec4(info->hdrData + 1) ;

    if (offset > info->lastOffset)
        info->lastOffset = offset ;

    zmodemlog("GotSendDoneAck[%s]: %ld\n", sname(info), info->offset) ;

    info->state = SendEof ;
    info->timeout = 60 ;
    return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info) ;
}

int Zmodem::GotSendNak(ZModem *info)
{
    info->offset = info->zrposOffset ;

    fseek(info->file, info->offset, 0) ;

    /* TODO: what if fseek fails?  Send EOF? */

    zmodemlog("GotSendNak[%s]: %ld\n", sname(info), info->offset) ;

    return SendMoreFileData(info) ;
}

int Zmodem::GotSendWaitAck(ZModem *info)
{

    ulong offset ;
    int err ;

    offset = ZDec4(info->hdrData + 1) ;

    qDebug("last=%lx, now=%lx", info->lastOffset, offset);

    if (offset > info->lastOffset)
        info->lastOffset = offset ;

    //receiver return -1 without setting this flag, kingson 00:07 14-07-04
    info->waitflag = 1 ;

    zmodemlog("GotSendWaitAck[%s]\n", sname(info), offset) ;

    if ((err = ZXmitHdr(ZDATA, ZBIN, info->hdrData + 1, info)))
        return err ;
    return SendMoreFileData(info) ;
}

int Zmodem::SkipFile(ZModem *info)
{
    zmodemlog("SkipFile[%s]\n", sname(info)) ;
    ZStatus(FileEnd, 0, info->rfilename);
    fclose(info->file) ;

    // stupid SMTH doesnt send further command, kick
    // lets send files in the list
    info-> state = TStart;
    return GotRinit(info);
}

int Zmodem::GotSendPos(ZModem *info)
{
    ZStatus(DataErr, ++info->errCount, NULL) ;
    info->waitflag = 1 ;  /* next pkt should wait, to resync */
    qDebug("GotSendPos, offset=%lx", info->offset);
    zmodemlog("GotSendPos[%s] %lx\n", sname(info), info->offset) ;
    return startFileData(info) ;
}

int Zmodem::SendFileData(ZModem *info)
{
    info->waitflag = 0 ;
    return startFileData(info) ;
}

int Zmodem::ResendEof(ZModem *info)
{
    return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info) ;
}

int Zmodem::OverAndOut(ZModem *info)
{
    zmodemlog("OverAndOut[%s]\n", sname(info)) ;

    ZXmitStr((uchar *)"OO", 2, info) ;

    transferstate = TransferStop;  // transfer complete

    ZmodemReset(info);  //Tranfer complete, zmodem return to receive state

    return ZmDone ;
}

int Zmodem::startFileData(ZModem *info)
{
    int err ;

    info->offset =
        info->lastOffset =
            info->zrposOffset = ZDec4(info->hdrData + 1) ;

    fseek(info->file, info->offset, 0) ;

    /* TODO: what if fseek fails?  Send EOF? */

    zmodemlog("startFileData[%s]: %lx\n", sname(info), info->offset) ;

    if ((err = ZXmitHdr(ZDATA, ZBIN, ZEnc4(info->offset), info)))
        return err ;
    return SendMoreFileData(info) ;
}

uchar * Zmodem::putHex(uchar *ptr, uchar c)
{
    *ptr++ = hexChars[(c>>4)&0xf] ;
    *ptr++ = hexChars[c&0xf] ;
    return ptr ;
}


int Zmodem::ZmodemReset(ZModem * info)
{
    zmodemlog("\nZmodemReset\n");

    ZmodemRInit(info);

    return 0;
}


void Zmodem::zmodemlog(const char *fmt, ...)
{
// only for debug
#ifdef QTERM_DEBUG
    va_list ap;
    struct timeval tv ;
    struct tm *tm ;
    static int do_ts = 1 ;

    zmodemlogfile = fopen("zmodem.log", "a");

    if (zmodemlogfile == NULL)
        return ;

    if (do_ts) {
        gettimeofday(&tv, NULL) ;
        tm = localtime(&tv.tv_sec) ;
        fprintf(zmodemlogfile, "%2.2d:%2.2d:%2.2d.%2.2ld: ",
                tm->tm_hour, tm->tm_min, tm->tm_sec,
                tv.tv_usec / 10000) ;
    }
    do_ts = strchr(fmt, '\n') != NULL ;

    va_start(ap, fmt) ;
    vfprintf(zmodemlogfile, fmt, ap) ;
    va_end(ap) ;

    fclose(zmodemlogfile);
#endif

}

void Zmodem::zmodemCancel()
{
    ZmodemAbort(&info);
}

void Zmodem::setFileList(const QStringList & fileList)
{
    strFileList = fileList;
}

} // namespace QTerm

#include <moc_qtermzmodem.cpp>
